Alex Liang

[Rails] 使用sidekiq處理mailer寄信功能

上一篇完成動態新增email欄位後,我們使用sidekiq和mailer完成非同步寄信功能
sidekiq官方建議使用JSON作為active job的參數,此篇也記錄將Model的參數轉換成需要的json格式

先從gem的基本安裝開始,此範例使用’letter_opener’和’sidekiq’
letter_opener可讓我們即時檢視信件,而不必真的寄信到信箱裡
sidekiq則是幫助我們將寄信的動作放到背景執行

Gem Install

Gemfile
1
2
gem 'letter_opener', group: :development
gem 'sidekiq'

使用bundle安裝gem

接著安裝Redis,sidekiq需要它才能運行

1
brew install redis

Mailer

1
rails g mailer UserMailer

設定config

config/environments/development.rb
1
2
config.action_mailer.default_url_options = { host: "http://localhost:3000" }
config.action_mailer.delivery_method = :letter_opener

設定發信的信箱位址

app/mailers/application_mailer.rb
1
2
3
4
class ApplicationMailer < ActionMailer::Base
default from: "[email protected]"
layout 'mailer'
end

設定寄信的收件人和主旨,這裡我們還需要目標的擁有者和目標的標題。share_goal會在之後的ActiveJob使用

app/mailers/user_mailer.rb
1
2
3
4
5
6
7
8
9
10
class UserMailer < ApplicationMailer
def share_goal(owner, goal, complete_date, email)
@owner = owner
@goal = goal
@complete_date = complete_date
@email = email

mail(to: @email, subject: "[ResolutionTracker] #{@owner}分享目標")
end
end

接著設定信件的內容,這裡會使用share_goal傳進來的參數

app/views/user_mailer/share_goal.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<h1>ResolutionTracker</h1>

<p>
Hi
</p>

<p>
以下是<%= @owner %>的目標:
<ul>
<li>目標:<%= @goal%></li>
<li>預計完成日期:<%= @complete_date %></li>
</ul>
目標連結:<%= link_to @goal, "#" %>
</p>

<p>
請好好督促 <%= @owner %> 喔!
</p>

Controller

在controller中需要增加一個action將active record轉成json格式再傳給ActiveJob

app/controllers/goals_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class GoalsController < ApplicationController
before_action :find_params, only: [:edit, :update, :destroy, :complete, :notify_friend]
...略

def notify_friend
mail_list = {}
count = 0
for email in @goal.shared_mails
mail_list[count.to_s] = email.mail_addr
count+=1
end

h = JSON.generate({ 'owner' => @goal.owner.name,
'goal' => @goal.title,
'complete_date' => @goal.complete_date,
'email' => mail_list })
SendEmailJob.perform_now(h)
redirect_to goals_path
end

private

def find_params
@goal = current_user.goals.find(params[:id])
end
end

由於一個目標可能會分享給多位朋友,我們創立一個空的hash table儲存各email address
接著使用ruby的JSON.generate產生active job參數。
這裡提到為什麼使用簡單的格式而不是整個物件當參數,原因是這會讓Redis serializing,加重系統負擔

Active Job

設定active job初始化

config/initializers/active_job.rb
1
2
3
4
5
if Rails.env.test?
ActiveJob::Base.queue_adapter = :inline
else
ActiveJob::Base.queue_adapter = :sidekiq
end

在app資料夾底下新增jobs資料夾,並新增send_email_job.rb

app/jobs/send_email_job.rb
1
2
3
4
5
6
7
8
9
class SendEmailJob < ActiveJob::Base
queue_as :default

def perform(h)
t = JSON.load(h)
t['email'].each_value {|value|
UserMailer.share_goal(t['owner'], t['goal'], t['complete_date'], value).deliver}
end
end

這裡接收controller傳來的json參數後,將其轉換成hash table,並對每一筆email位址發送信件

驗證

要驗證前得分別啟動redis server和sidekiq

1
redis server

1
bundle exec sidekiq

螢幕快照 2016-04-14 下午3.27.10.png

參考來源:
Action Mailer Basics
blog參考文章
sidekiq github